#ifndef _HOOK_H_
#define _HOOK_H_

#include "tools/helpMacros.h"
#include "tools/locker.h"

#include <string>

#define BOOST_FT_COMMON_X86_CCs 1
#define FUSION_MAX_VECTOR_SIZE 20
#define BOOST_FUNCTION_MAX_ARGS 20
#define BOOST_FUSION_INVOKE_MAX_ARITY 20
#define BOOST_RESULT_OF_NUM_ARGS 20

#include <boost/function.hpp>
#include <boost/function_types/config/config.hpp>
#include <boost/fusion/include/mpl.hpp> 
#include <boost/fusion/include/as_vector.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <boost/mpl\size.hpp>
#include <boost/function_types\parameter_types.hpp>
#include <boost/function_types\result_type.hpp >
#include <boost/function_types/function_type.hpp>
#include <boost/fusion\container.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/fusion/functional/invocation/invoke.hpp>
#include <boost/fusion/include/mpl.hpp>


namespace sd
{
namespace hp
{

#define SD_HP_CREATE_HOOK( hook, libName )	sd::hp::HookedProcess::getInstance().getHook<decltype(hook)>( #hook, libName )

namespace impl
{
class HookImplementation;
typedef boost::shared_ptr<HookImplementation> HookImplementationRef;
}



class FunctionIsNowCalled : public std::exception
{
public:
	FunctionIsNowCalled(const std::string& name, const std::string& library) : name_(name), library_(library){}
	virtual const char * __CLR_OR_THIS_CALL what() const
	{
		if(text_.empty())
			text_ = std::string("Setring hook fail: Function [") + name_ + "] from library [" + library_ + "] is now called";
		return text_.c_str();
	}
private:
	std::string name_;
	std::string library_;
	mutable std::string text_;

};



class HookBase
{
public:

	std::string getName()const{ return name_; }
	std::string getLibrary()const{ return library_; }

	bool isActive()const;
	void setActive( bool aactive );

	bool operator ==( const HookBase& Ref )const{ return name_ == Ref.name_ && library_ == Ref.library_; }
	bool operator <( const HookBase& Ref )const{ return name_ != Ref.name_ ? name_ < Ref.name_ : library_ < Ref.library_; }

protected:
	HookBase( const std::string& name, const std::string& libName ): name_( name ), library_( libName ), active_(false){}
	void setHook();
	void removeHook();
	void createImpl();

	template<typename T>
	friend class tools::locker;

	void lock();
	void unlock();

	virtual void* getInnerCallbackPtrVirt()=0;
	virtual void  setDummyActive(bool)=0;

	void* getOriginalPtr()const;
private:
	friend class HookedProcess;
	SD_TL_EMPTY_DEF_MEMBERS( HookBase );
	void innerSetActive( bool iactive );

	const std::string                     name_;
	const std::string                  library_;
	bool  active_;
	impl::HookImplementationRef impl_;
};



template<  typename TProc >
class Hook: public HookBase
{

public:
	typedef Hook< TProc > THook;

	typedef typename boost::function_types::parameter_types< TProc > ParamMplList;

	typedef typename boost::function_types::result_type< TProc >::type ResultType;

	typedef typename boost::fusion::result_of::as_vector< ParamMplList >::type ParamList;

	typedef typename boost::fusion::result_of::push_front< ParamList, ResultType >::type TTmpRes;

	typedef typename boost::function_types::function_type< TTmpRes >::type TSyntProc;
	
	typedef typename boost::function< TSyntProc > HookProc;

	typedef typename boost::function< ResultType(THook&,ParamList&) > SynthHookProc;

	struct SynthFunctor
	{
		SynthFunctor(const HookProc& callback):callback_(callback){}
		inline ResultType operator()( THook&,ParamList& params ){ return boost::fusion::invoke( callback_, params ); }
	private:		
		const HookProc callback_;
	};


	struct OriginalCaller: public SynthFunctor
	{
		OriginalCaller(const THook& hook):SynthFunctor( hook.getOriginal() ){}
	};


	Hook( const std::string& name, const std::string& libName ): HookBase( name, libName ){}

	enum{ ParamCount = boost::mpl::size< ParamList >::value };

	// Function or functor
	//

	void setCallback( const HookProc& callback )
	{
		setCallbackSynth( SynthFunctor( callback ) );
	}


	void setCallbackSynth( const SynthHookProc& callback )
	{
		if(isActive())
		{
			tools::locker< HookBase > locker(*this);
			callback_ = callback;
		}else
			callback_ = callback;
	}

	TProc* getOriginal()const{ return static_cast<TProc*>( getOriginalPtr() ); }

	

protected:

	static void* __cdecl innerCallback( void* userData, int* workedParams,
	void* /*reserv1*/, void* reserv2, ... );

	void* getInnerCallbackPtr(){ return &innerCallback; }
	virtual void* getInnerCallbackPtrVirt(){ return getInnerCallbackPtr(); }

	virtual void  setDummyActive(bool active);


private:
	SD_TL_EMPTY_DEF_MEMBERS( Hook );
	SynthHookProc callback_;
	SynthHookProc store_callback_;

};




}// namespace hp
}// namespace sd

#include "Hook.hpp"















#endif//_HOOK_H_